﻿// -------------------------------------------------------------------------------------------------------------------------------
// <copyright file="TestSubtractIndexedRMDenseMatrixFactor.cs" company="Exul">
//     Copyright (c) Exul. All rights reserved.
// </copyright>
// <author>
//     Exul
// </author>
// -------------------------------------------------------------------------------------------------------------------------------

namespace ExulLibrary.Mathematics.LinearAlgebra.Sequential.Tests.RMDenseMatrixTest.DoubleExtensions
{
    #region Usings

    using System;
    using System.Globalization;

    using ExulLibrary.Exceptions;
    using ExulLibrary.Mathematics.LinearAlgebra.Sequential.DoubleExtensions;

    using NUnit.Framework;

    #endregion

    /// <summary>
    ///     Test suite for
    ///     <see cref="RMDenseMatrixExtensions.Subtract(RMDenseMatrix{double},int[],int[],RMDenseMatrix{double},double)"/>.
    /// </summary>
    [TestFixture]
    [Category("ExulLibrary.Mathematics.LinearAlgebra.Sequential.DoubleExtensions.RMDenseMatrixExtensions")]
    public sealed class TestSubtractIndexedRMDenseMatrixFactor
    {
        /// <summary>
        ///     Tests the method when indexes of minuend elements are non-negative and less than size of dimensions
        ///     of minuend matrix.
        /// </summary>
        [Test]
        public void WhenMinuendIndexesAreValid()
        {
            // Arrange
            const double Factor = 2;
            var minuendData = new[]
                              {
                                  new double[] { 10, 20, 30 },
                                  new double[] { 40, 50, 60 },
                                  new double[] { 70, 80, 90 }
                              };
            var minuend = new RMDenseMatrix<double>(minuendData);

            var minuendRowsIndexes = new[] { 0, 2 };
            var minuendColumnsIndexes = new[] { 1, 2 };
            var subtrahendData = new[]
                                 {
                                     new double[] { 1, 2 },
                                     new double[] { 3, 4 }
                                 };
            var subtrahend = new RMDenseMatrix<double>(subtrahendData);
            var expectedResult = new[]
                                 {
                                     new double[] { 10, 18, 26 },
                                     new double[] { 40, 50, 60 },
                                     new double[] { 70, 74, 82 }
                                 };

            // Act
            minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, subtrahend, Factor);

            // Assert
            Assert.That(minuend.Values, Is.EquivalentTo(expectedResult));
        }

        /// <summary>
        ///     Tests the method when minuend rows indexes collection is empty.
        /// </summary>
        [Test]
        public void WhenRowsIndexesCollectionIsEmpty()
        {
            // Arrange
            const double Factor = 2;
            var minuendData = new[]
                              {
                                  new double[] { 10, 20, 30 },
                                  new double[] { 40, 50, 60 },
                                  new double[] { 70, 80, 90 }
                              };
            var minuend = new RMDenseMatrix<double>(minuendData);

            var minuendRowsIndexes = new int[0];
            var minuendColumnsIndexes = new[] { 1, 2 };
            var subtrahendData = new[]
                                 {
                                     new double[] { 1, 2 },
                                     new double[] { 3, 4 }
                                 };
            var subtrahend = new RMDenseMatrix<double>(subtrahendData);
            var expectedResult = new[]
                                 {
                                     new double[] { 10, 20, 30 },
                                     new double[] { 40, 50, 60 },
                                     new double[] { 70, 80, 90 }
                                 };

            // Act
            minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, subtrahend, Factor);

            // Assert
            Assert.That(minuend.Values, Is.EquivalentTo(expectedResult));
        }

        /// <summary>
        ///     Tests the method when minuend columns indexes collection is empty.
        /// </summary>
        [Test]
        public void WhenColumnsIndexesCollectionIsEmpty()
        {
            // Arrange
            const double Factor = 2;
            var minuendData = new[]
                              {
                                  new double[] { 10, 20, 30 },
                                  new double[] { 40, 50, 60 },
                                  new double[] { 70, 80, 90 }
                              };
            var minuend = new RMDenseMatrix<double>(minuendData);

            var minuendRowsIndexes = new[] { 0, 2 };
            var minuendColumnsIndexes = new int[0];
            var subtrahendData = new[]
                                 {
                                     new double[] { 1, 2 },
                                     new double[] { 3, 4 }
                                 };
            var subtrahend = new RMDenseMatrix<double>(subtrahendData);
            var expectedResult = new[]
                                 {
                                     new double[] { 10, 20, 30 },
                                     new double[] { 40, 50, 60 },
                                     new double[] { 70, 80, 90 }
                                 };

            // Act
            minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, subtrahend, Factor);

            // Assert
            Assert.That(minuend.Values, Is.EquivalentTo(expectedResult));
        }

#if SAFE

        /// <summary>
        ///     Tests the method when minuend is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenMinuendIsNull()
        {
            // Arrange
            const double Factor = 2;
            var minuendRowsIndexes = new[] { 0, 2 };
            var minuendColumnsIndexes = new[] { 1, 2 };
            var subtrahendData = new[]
                                 {
                                     new double[] { 1, 2 },
                                     new double[] { 3, 4 }
                                 };
            var subtrahend = new RMDenseMatrix<double>(subtrahendData);

            // Act and Assert
            Assert.That(
                () => RMDenseMatrixExtensions.Subtract(null, minuendRowsIndexes, minuendColumnsIndexes, subtrahend, Factor),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("minuend"));
        }

        /// <summary>
        ///     Tests the method when minuend is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenSubtrahendIsNull()
        {
            // Arrange
            const double Factor = 2;
            var minuendData = new[]
                              {
                                  new double[] { 10, 20, 30 },
                                  new double[] { 40, 50, 60 },
                                  new double[] { 70, 80, 90 }
                              };
            var minuend = new RMDenseMatrix<double>(minuendData);

            var minuendRowsIndexes = new[] { 0, 2 };
            var minuendColumnsIndexes = new[] { 1, 2 };

            // Act and Assert
            Assert.That(
                () => minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, null, Factor),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("subtrahend"));
        }

        /// <summary>
        ///     Tests the method when minuend rows indexes collection is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenMinuendRowsIndexesCollectionIsNull()
        {
            // Arrange
            const double Factor = 2;
            var minuendData = new[]
                              {
                                  new double[] { 10, 20, 30 },
                                  new double[] { 40, 50, 60 },
                                  new double[] { 70, 80, 90 }
                              };
            var minuend = new RMDenseMatrix<double>(minuendData);

            var minuendColumnsIndexes = new[] { 1, 2 };
            var subtrahendData = new[]
                                 {
                                     new double[] { 1, 2 },
                                     new double[] { 3, 4 }
                                 };
            var subtrahend = new RMDenseMatrix<double>(subtrahendData);

            // Act and Assert
            Assert.That(
                () => minuend.Subtract(null, minuendColumnsIndexes, subtrahend, Factor),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("minuendRowsIndexes"));
        }

        /// <summary>
        ///     Tests the method when minuend columns indexes collection is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenMinuendColumnsIndexesCollectionIsNull()
        {
            // Arrange
            const double Factor = 2;
            var minuendData = new[]
                              {
                                  new double[] { 10, 20, 30 },
                                  new double[] { 40, 50, 60 },
                                  new double[] { 70, 80, 90 }
                              };
            var minuend = new RMDenseMatrix<double>(minuendData);

            var minuendRowsIndexes = new[] { 0, 2 };
            var subtrahendData = new[]
                                 {
                                     new double[] { 1, 2 },
                                     new double[] { 3, 4 }
                                 };
            var subtrahend = new RMDenseMatrix<double>(subtrahendData);

            // Act and Assert
            Assert.That(
                () => minuend.Subtract(minuendRowsIndexes, null, subtrahend, Factor),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("minuendColumnsIndexes"));
        }

        /// <summary>
        ///     Tests the method when a minuend row index is negative or greater than or equal to rows count of minuend matrix.
        /// </summary>
        /// <param name="rowIndex">
        ///     The row index.
        /// </param>
        /// <param name="location">
        ///     The location of row index.
        /// </param>
        [Test]
        public void WhenAMinuendRowIndexIsOutOfRange([Values(-1, 3)] int rowIndex, [Values(0, 1)] int location)
        {
            // Arrange
            const double Factor = 2;
            var minuendData = new[]
                              {
                                  new double[] { 10, 20, 30 },
                                  new double[] { 40, 50, 60 },
                                  new double[] { 70, 80, 90 }
                              };
            var minuend = new RMDenseMatrix<double>(minuendData);

            var minuendRowsIndexes = new int[2];
            minuendRowsIndexes[location] = rowIndex;

            var minuendColumnsIndexes = new[] { 1, 2 };
            var subtrahendData = new[]
                                 {
                                     new double[] { 1, 2 },
                                     new double[] { 3, 4 }
                                 };
            var subtrahend = new RMDenseMatrix<double>(subtrahendData);

            // Act and Assert
            var argumentName = string.Format(CultureInfo.InvariantCulture, "minuendRowsIndexes[{0}]", location);
            Assert.That(
                () => minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, subtrahend, Factor),
                Throws.TypeOf<ElementIndexIsOutOfRangeException>().With.Property("ParamName").EqualTo(argumentName));
        }

        /// <summary>
        ///     Tests the method when a minuend column index is negative or greater than or equal to
        ///     columns count of minuend matrix.
        /// </summary>
        /// <param name="columnIndex">
        ///     The column index.
        /// </param>
        /// <param name="location">
        ///     The location of column index.
        /// </param>
        [Test]
        public void WhenAMinuendColumnIndexIsOutOfRange([Values(-1, 3)] int columnIndex, [Values(0, 1)] int location)
        {
            // Arrange
            var minuendData = new[]
                              {
                                  new double[] { 10, 20, 30 },
                                  new double[] { 40, 50, 60 },
                                  new double[] { 70, 80, 90 }
                              };
            var minuend = new RMDenseMatrix<double>(minuendData);

            var minuendRowsIndexes = new[] { 0, 2 };
            var minuendColumnsIndexes = new int[2];
            minuendColumnsIndexes[location] = columnIndex;

            var subtrahendData = new[]
                                 {
                                     new double[] { 1, 2 },
                                     new double[] { 3, 4 }
                                 };
            var subtrahend = new RMDenseMatrix<double>(subtrahendData);

            // Act and Assert
            const double Factor = 2;
            var argumentName = string.Format(CultureInfo.InvariantCulture, "minuendColumnsIndexes[{0}]", location);
            Assert.That(
                () => minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, subtrahend, Factor),
                Throws.TypeOf<ElementIndexIsOutOfRangeException>().With.Property("ParamName").EqualTo(argumentName));
        }

#endif
    }
}